use jsonschema::Validator;

use super::{actions::FormCommand, state::FormState};

pub fn apply_command(state: &mut FormState, command: FormCommand) {
    match command {
        FormCommand::FocusNextField => state.focus_next_field(),
        FormCommand::FocusPrevField => state.focus_prev_field(),
        FormCommand::FocusNextSection(delta) => state.focus_next_section(delta),
        FormCommand::FocusNextRoot(delta) => state.focus_next_root(delta),
        FormCommand::FieldEdited { .. } => {}
    }
}

pub struct FormEngine<'a> {
    state: &'a mut FormState,
    validator: &'a Validator,
}

impl<'a> FormEngine<'a> {
    pub fn new(state: &'a mut FormState, validator: &'a Validator) -> Self {
        Self { state, validator }
    }

    pub fn dispatch(&mut self, command: FormCommand) -> Result<(), String> {
        match command {
            FormCommand::FieldEdited { pointer } => self.validate_field(&pointer),
            other => {
                apply_command(self.state, other);
                Ok(())
            }
        }
    }

    fn validate_field(&mut self, pointer: &str) -> Result<(), String> {
        match self.state.try_build_value() {
            Ok(value) => {
                self.state.clear_error(pointer);
                let mut matched = false;
                for error in self.validator.iter_errors(&value) {
                    let err_pointer = error.instance_path.to_string();
                    if err_pointer == pointer {
                        matched = true;
                        self.state.set_error(&err_pointer, error.to_string());
                    }
                }
                if !matched {
                    self.state.clear_error(pointer);
                }
                Ok(())
            }
            Err(err) => {
                self.state.set_error(&err.pointer, err.message.clone());
                Err(err.message)
            }
        }
    }
}
